package com.leinao.media.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.leinao.media.callback.FMSGCallBack;
import com.leinao.media.callback.RealTimeThermometryCallBack;
import com.leinao.media.constants.MediaConstant;
import com.leinao.media.entity.*;
import com.leinao.media.exception.MediaException;
import com.leinao.media.service.MediaService;
import com.leinao.media.service.RedisService;
import com.leinao.media.utils.MediaUtil;
import com.leinao.media.utils.NullUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;

/**
 * 流媒体Service接口实现类
 *
 * @author zhangyu
 * @date 2021/12/1 16:00
 */
@Service
public class MediaServiceImpl implements MediaService {

    private static final Logger logger = LoggerFactory.getLogger(MediaServiceImpl.class);

    @Autowired
    private RedisService redisService;

    @Value("${file.saveAddress}")
    private String saveAddress;

    @Value("${ip_port}")
    private String ip_port;

    @Autowired
    private FMSGCallBack callBack;

    @Autowired
    private RealTimeThermometryCallBack timeThermometryCallBack;

    /**
     * 录像缓存，key-开始录像返回值，value-录像保存地址
     */
    private final static HashMap<String, String> MAKE_VIDEO_CASH = new HashMap<>();

    @Override
    public void activateDevice(BaseDeviceParam param) {
        logger.info("激活设备，ip：{}，port，{}，password：{}", param.getIp(), param.getPort(), param.getPassword());
        String result = MediaUtil.activateDevice(param.getIp(), param.getPort(), param.getPassword(), param.getType());
        if (StrUtil.isNotBlank(result)) {
            throw new MediaException(result);
        }
        // 激活注册
//        this.login(param);
    }

    @Override
    public long login(BaseDeviceParam param) {
        Integer type = param.getType();
        long uid = MediaUtil.login(param.getIp(), param.getPort(), param.getUserName(), param.getPassword(), type);
        // 缓存uid
        if (type == MediaConstant.NVR_TYPE_HK) {
            int hkUid = (int) uid;
            redisService.setCacheObject(MediaConstant.NVR_CACHE_PREFIX + param.getIp(), hkUid);
            return hkUid;
        } else {
            redisService.setCacheObject(MediaConstant.NVR_CACHE_PREFIX + param.getIp(), uid);
            return uid;
        }
    }

    @Override
    public void logout(LogoutParam param) {
        MediaUtil.logout(param.getUid(), param.getType());
    }

    @Override
    public void cleanup(int type) {
        MediaUtil.cleanup(type);
    }

    @Override
    public String getDeviceStatus(BaseDeviceParam param) {
        long uid = this.getNvrUidFromCache(param.getIp(), param.getPort(), param.getUserName(), param.getPassword(), param.getType());
        return MediaUtil.getDeviceStatus(uid, param.getType());
    }

    @Override
    public List<Camera> getCameraList(BaseDeviceParam param) {
        long uid = this.getNvrUidFromCache(param.getIp(), param.getPort(), param.getUserName(), param.getPassword(), param.getType());
        return MediaUtil.getCameraList(param.getIp(), uid, param.getUserName(), param.getPassword(), param.getType());
    }

    @Override
    public String cloudPlatformControl(CloudPlatformControlParam param) {
        long uid = this.getNvrUidFromCache(param.getIp(), param.getPort(), param.getUserName(), param.getPassword(), param.getType());
        return MediaUtil.cloudPlatformControl(param.getType(), uid, param.getChannel(), param.getCommand(), param.getIsStop(), param.getSpeed());
    }

    @Override
    public void setPresetLocation(PresetLocationParam param) {
        long uid = this.getNvrUidFromCache(param.getIp(), param.getPort(), param.getUserName(), param.getPassword(), param.getType());
        logger.info("设置相机的PresetLocation：{}", JSON.toJSONString(param));
        MediaUtil.setPresetLocation(uid, param.getChannel(), param.getCommand(), param.getIndex(), param.getType());
    }

    @Override
    public JSONObject searchVideoTime(VideoQueryParam param) {
        long uid = this.getNvrUidFromCache(param.getIp(), param.getPort(), param.getUserName(), param.getPassword(), param.getType());
        return MediaUtil.searchVideoTime(uid, param.getChannel());
    }

    @Override
    public JSONObject getVideoMonth(VideoQueryParam param) {
        JSONObject jsonObject = new JSONObject(true);
        long uid = this.getNvrUidFromCache(param.getIp(), param.getPort(), param.getUserName(), param.getPassword(), param.getType());
        JSONArray videoMonth = MediaUtil.getVideoMonth(uid, param.getChannel(), param.getYear(), param.getMonth(), param.getType());
        for (int i = 0; i < Objects.requireNonNull(videoMonth).size(); i++) {
            JSONObject jsonObject1 = videoMonth.getJSONObject(i);
            Object o = jsonObject1.get(i + 1);
            String key = NullUtil.valueOf(i + 1);
            if (Integer.parseInt(key) < 10) {
                key = "0" + key;
            }
            jsonObject.put(key, o);
        }
        return jsonObject;
    }

    @Override
    public JSONObject findVideoFile(VideoQueryParam param) {
        long uid = this.getNvrUidFromCache(param.getIp(), param.getPort(), param.getUserName(), param.getPassword(), param.getType());
        return MediaUtil.findVideoFile(uid, param.getChannel(), param.getStartTime(), param.getEndTime(), param.getType());
    }

    @Override
    public JSONObject downloadVideoFileByTime(VideoDownloadParam param) {
        // 保存路径
        String path = saveAddress + "video" + File.separator;
        FileUtil.mkdir(path);
        long uid = this.getNvrUidFromCache(param.getIp(), param.getPort(), param.getUserName(), param.getPassword(), param.getType());

        String fileName = param.getFileName();
        Date startTime = param.getStartTime();
        Date endTime = param.getEndTime();

        String saveFileName = path + fileName + ".avi";
        new File(saveFileName);
        param.setSaveAddress(saveFileName);

        long iDownHandle = MediaUtil.downloadVideoFileByTime(uid, param.getChannel(), fileName, startTime, endTime, param.getSaveAddress(), param.getType());

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("filePath", param.getSaveAddress());
        jsonObject.put("iDownHandle", iDownHandle);

        return jsonObject;
    }

    @Override
    public int getDownLoadProgress(DownLoadProgressParam param) {
        return MediaUtil.getDownLoadProgress(param.getiDownHandle(), param.getType());
    }

    @Override
    public void stopDownloadVideoFile(BaseDeviceParam param) {
        MediaUtil.stopDownloadVideoFile(param.getType());
    }

    @Override
    public JSONObject startMakeVideo(VideoQueryParam param) {
        long uid = this.getNvrUidFromCache(param.getIp(), param.getPort(), param.getUserName(), param.getPassword(), param.getType());
        String path = saveAddress + "video" + File.separator + DateUtil.format(new Date(), "yyyy-MM-dd") + File.separator;
        FileUtil.mkdir(path);
        // 视频名
        String videoName = path + System.currentTimeMillis() + ".avi";
        param.setSaveAddress(videoName);
        long lPreviewHandle = MediaUtil.startMakeVideo(uid, param.getChannel(), param.getSaveAddress(), param.getType());
        // 缓存录像保存路径
        MAKE_VIDEO_CASH.put(NullUtil.valueOf(lPreviewHandle), videoName);

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("filePath", param.getSaveAddress());
        jsonObject.put("lPreviewHandle", lPreviewHandle);

        return jsonObject;
    }

    @Override
    public String stopMakeVideo(VideoQueryParam param) {
        long lPreviewHandle = param.getlPreviewHandle();
        MediaUtil.stopMakeVideo(lPreviewHandle, param.getType());
        return ip_port + MAKE_VIDEO_CASH.get(NullUtil.valueOf(lPreviewHandle));
    }

    @Override
    public String captureJpeg(CaptureJpegParam param) {
        logger.info("截图参数：{}", JSON.toJSONString(param));
        String path = saveAddress + "capture" + File.separator + DateUtil.format(new Date(), "yyyy-MM-dd") + File.separator;
        FileUtil.mkdir(path);
        param.setSaveAddress(path);
        long uid = this.getNvrUidFromCache(param.getIp(), param.getPort(), param.getUserName(), param.getPassword(), param.getType());
        return ip_port + MediaUtil.captureJpeg(uid, param.getChannel(), param.getSaveAddress(), param.getQuality(), param.getType());
    }

    @Override
    public String captureJpegNew(CaptureJpegParam param) {
        long uid = this.getNvrUidFromCache(param.getIp(), param.getPort(), param.getUserName(), param.getPassword(), param.getType());
        return MediaUtil.captureJpeg(uid, param.getChannel(), param.getSaveAddress(), param.getQuality(), param.getType());
    }

    @Override
    public String captureJpegStream(CaptureJpegParam param) {
        logger.info("截图参数：{}", JSON.toJSONString(param));
        String path = saveAddress + "capture" + File.separator + DateUtil.format(new Date(), "yyyy-MM-dd") + File.separator;
        FileUtil.mkdir(path);
        param.setSaveAddress(path);
        long uid = this.getNvrUidFromCache(param.getIp(), param.getPort(), param.getUserName(), param.getPassword(), param.getType());
        String picName = MediaUtil.captureJpeg(uid, param.getChannel(), param.getSaveAddress(), param.getQuality(), param.getType());
        // 获取图片二进制流
        return this.getImageBinary(picName);
    }

    @Override
    public String getStreamCaptureJpeg(CaptureJpegStreamParam param) {
        try {
            byte[] bytes1 = new sun.misc.BASE64Decoder().decodeBuffer(param.getStreamStr());
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes1);
            BufferedImage bi1 = ImageIO.read(bais);
            File w2 = new File(param.getPicName());
            // 不管输出什么格式图片，此处不需改动
            ImageIO.write(bi1, "jpg", w2);
        } catch (IOException e) {
            logger.error("二进制流转图片出现错误，错误原因：{}", e.getMessage());
            throw new MediaException("二进制流转图片出现错误");
        }
        return param.getPicName();
    }

    @Override
    public JSONObject getDeviceDiskInfo(BaseDeviceParam param) {
        JSONObject jsonObject = new JSONObject();
        long uid = this.getNvrUidFromCache(param.getIp(), param.getPort(), param.getUserName(), param.getPassword(), param.getType());
        if (MediaConstant.HK_ERROR_UID == uid) {
            return jsonObject;
        }
        // 设备类型
        Integer type = param.getType();
        // 获取设备硬盘信息
        jsonObject = MediaUtil.getDeviceDiskInfo(uid, type);
        // 获取录像总时长
        long sumTime = MediaUtil.getSumVideoTime(param.getIp(), uid, param.getUserName(), param.getPassword(), type);
        // 4.汇总所有的摄像机的录像时长
        jsonObject.put("sumTime", sumTime);
        return jsonObject;
    }

    @Override
    public String getPlayBackAddress(VideoPlayBackParam param) {
        return MediaUtil.getPlayBackAddress(param.getIp(), param.getUserName(), param.getPassword(),
                param.getId(), param.getChannel(), param.getStartTime(), param.getEndTime(), param.getType());
    }

    @Override
    public void setTemperatureRule(TemperatureRuleParam param) {
        long uid = -1;
        try {
            uid = MediaUtil.login(param.getIp(), param.getPort(), param.getUserName(), param.getPassword(), param.getType());
//            uid = this.getNvrUidFromCache(param.getIp());
            TemperatureRule rule = new TemperatureRule();
            BeanUtils.copyProperties(param, rule);
            rule.setUid(uid);
            logger.info(rule.toString());
            boolean bool = MediaUtil.makeTemperatureRule(rule);
            if (!bool) {
                logger.error("设置测温规则失败");
            }
        } finally {
            MediaUtil.logout(uid, param.getType());
        }
    }

    @Override
    public int setUpAlarm(BaseDeviceParam param) {
        logger.info("红外相机{}，开启布防", param.getIp());
        long uid = this.getNvrUidFromCache(param.getIp(), param.getPort(), param.getUserName(), param.getPassword(), param.getType());
        return MediaUtil.setUpAlarm(uid, param.getIp(), callBack);
    }

    @Override
    public boolean closeAlarmChan(CloseAlarmChanParam param) {
        return MediaUtil.closeAlarmChan(param.getlAlarmHandle());
    }

    @Override
    public int setUpThermometry(String ip, long uid) {
        logger.info("红外相机{}，开启实时测温", ip);
        return MediaUtil.setUpThermometry(ip, uid, timeThermometryCallBack);
    }

    @Override
    public boolean closeThermometry(int handle) {
        return MediaUtil.closeThermometry(handle);
    }

    /**
     * 从缓存中获取nvr的uid
     *
     * @param ip 设备ip
     * @return uid
     */
    private long getNvrUidFromCache(String ip, String port, String userName, String password, int type) {
        Object uid = redisService.getCacheObject(MediaConstant.NVR_CACHE_PREFIX + ip);
        // 如果redis中数据为空，则说明未注册，需要重新注册，再放入redis中
        if (null == uid) {
            long newUid = MediaUtil.login(ip, port, userName, password, type);
            // 缓存uid
            if (type == MediaConstant.NVR_TYPE_HK) {
                redisService.setCacheObject(MediaConstant.NVR_CACHE_PREFIX + ip, (int) newUid);
            } else {
                redisService.setCacheObject(MediaConstant.NVR_CACHE_PREFIX + ip, newUid);
            }
            return newUid;
        }
        if (uid instanceof Integer) {
            return (int) uid;
        } else {
            return (long) uid;
        }
    }

    /**
     * 获取图片二进制流
     *
     * @param picName 图片路径
     * @return 图片二进制流
     */
    private String getImageBinary(String picName) {
        File f = new File(picName);
        BufferedImage bi;
        try {
            bi = ImageIO.read(f);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ImageIO.write(bi, "jpg", baos);
            byte[] bytes = baos.toByteArray();
            return new sun.misc.BASE64Encoder().encodeBuffer(bytes).trim();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

}
